#include "ClefPrivee.h"

ClefPrivee * ClefPriveeGenerer(ClefPublique * pClefPublique)
{
    // On récupère c et m de la clef publique qui sera associée à la clef privée
    mpz_t c;
    mpz_t m;
    mpz_init_set(c, ClefPubliqueGetc(pClefPublique));
    mpz_init_set(m, ClefPubliqueGetm(pClefPublique));

//    gmp_printf("voici c: %Zd \n", c);
//    gmp_printf("voici m: %Zd \n", m);
//     gmp_printf("voici n: %Zd \n", ClefPubliqueGetn(pClefPublique));

    mpz_t q;
    mpz_t r;
    mpz_t u;
    mpz_t v;
    mpz_init(q);
    mpz_init(r);
    mpz_init_set_ui(u, 1);
    mpz_init_set_ui(v, 0);

    //variables tampons
    mpz_t x;
    mpz_t y;
    mpz_t z;
    mpz_t t;
    mpz_init_set_ui(x, 0);
    mpz_init_set_ui(y, 1);
    mpz_init(z);
    mpz_init(t);

    /* variables pour compenser la syntaxe gmp */
    mpz_t m1;
    mpz_t m2;
    mpz_init(m1);
    mpz_init(m2);

    /*On recherche u tel que cu +mv=1 avec 2<u<m */
    if (mpz_cmp(c,m)>0)
    {
        /* On s'arrête lorsque m<=0 */
        while (mpz_cmp_ui(m, 0)>0)
        {
            /* On met dans r le reste de la division euclidienne de c par m */
            /* et dans q le quotient de la division euclidienne de c par m */
            mpz_fdiv_qr(q, r, c, m);

            /* Au lieu de remonter l'algorithme d'euclide on trouve u et v en descendant */
            mpz_set(z ,u);
            mpz_set(t, v);
            mpz_set(u, x);
            mpz_set(v, y);

            /* On fait x=z-q*x */
            mpz_mul(m1, q, x);
            mpz_sub(x, z, m1);
            /* On fait y=t-q*y */
            mpz_mul(m2, q, y);
            mpz_sub(y, t, m2);

            /* On met m dans c et r dans m */
            mpz_set(c, m);
            mpz_set(m, r);
        }
    }
    else
    {
        /* On s'arrête lorsque c<=0 */
        while (mpz_cmp_ui(c, 0)>0)
        {
            /* On met dans r le reste de la division euclidienne de c par m */
            /* et dans q le quotient de la division euclidienne de c par m */
            mpz_fdiv_qr(q, r, m, c);

            /* Au lieu de remonter l'algorithme d'euclide on trouve u et v en descendant*/
            mpz_set(z ,u);
            mpz_set(t, v);
            mpz_set(u, x);
            mpz_set(v, y);

            /* On fait x=z-q*x */
            mpz_mul(m1, q, x);
            mpz_sub(x, z, m1);

            /* On fait y=t-q*y */
            mpz_mul(m2, q, y);
            mpz_sub(y, t, m2);

            /* On met m dans c et r dans m */
            mpz_set(m, c);
            mpz_set(c, r);
        }
        /* On veut avoir cu+mv=1 et non mu+cv=1. Car le coefficient qui nous intéresse est celui devant le c et pour la suite se sera u. */
        mpz_set(m1, u);
        mpz_set(u, v);
        mpz_set(v, m1);
    }

    /* On remet les bonne valeur dans c et m */
    mpz_set(c, ClefPubliqueGetc(pClefPublique));
    mpz_set(m, ClefPubliqueGetm(pClefPublique));

//    gmp_printf("voici c: %Zd \n", c);
//    gmp_printf("voici m: %Zd \n", m);
//    gmp_printf("voici u: %Zd \n", u);
//    gmp_printf("voici v: %Zd \n", v);

    /* On a maintenat u et v tel que cu + mv = 1 */

    /* On veut maintenant avoir 2<u<m
    On a 1 = cu+mv = c(u-k*m)+m(v+k*c) où k est un entier
    Par suite on peut ajouter un multiple quelconque de m à u pour avoir un u qui convient */

	if(mpz_cmp_ui(u,2)<0)
	{
		mpz_t k;
		mpz_init(k);

		/* Nous avons u < 2 ; on veut
        (u-km) > 2
        ie : -km > 2 - u
        ie : k < (2 - u)/(-m)  */

        mpz_ui_sub(k,(unsigned long int)2,u);
        mpz_neg(m, m);
		mpz_div(k, k, m);

//		gmp_printf("voici k: %Zd \n", k);

        /*On remet à m sa valeur initial ie -m: */
        mpz_neg(m, m);

        /* On modifie la valeur de u: u=u-k*m */
        mpz_mul(m1, k, m);

        /*gmp_printf("voici m1: %Zd \n", m1) */
        mpz_sub(u, u, m1);

        /* On libère k */
        mpz_clear(k);
	}

//    gmp_printf("voici u: %Zd \n", u);

	if(mpz_cmp(u, m)>0)
	{
		mpz_t k;
		mpz_init(k);

		/* Nous avons u > b ; on veut :
        (u - km) > m
        ie : -km > m - u
        ie : k < (m-u)/(-m)  */

        mpz_sub(k,m,u);

        /* gmp_printf("voici k: %Zd \n", k) */

		mpz_neg(m, m);
		mpz_div(k, k, m);

        /* On remet à m sa valeur initial ie -m: */
        mpz_neg(m, m);

        /* On modifie la valeur de u: u=u-k*m */
        mpz_mul(m1, k, m);

        /* gmp_printf("voici m1: %Zd \n", m1) */
        mpz_sub(u, u, m1);

        /* On libère k */
        mpz_clear(k);
	}

//    gmp_printf("voici u: %Zd \n", u);
//    gmp_printf("voici n: %Zd \n", ClefPubliqueGetn(pClefPublique));

    /* On crée notre clef privee */
    ClefPrivee *clef;
    ClefPriveeInitialiser(&clef);
	ClefPriveeSetn(clef, ClefPubliqueGetn(pClefPublique));
	ClefPriveeSetu(clef, u);

	/* On libère nos variables */
	mpz_clear(c);
	mpz_clear(m);
	mpz_clear(q);
	mpz_clear(r);
	mpz_clear(u);
	mpz_clear(v);
	mpz_clear(x);
	mpz_clear(y);
	mpz_clear(z);
	mpz_clear(t);
	mpz_clear(m1);
	mpz_clear(m2);

	return clef;
}


void ClefPriveeAfficher(const ClefPrivee * pClefPrivee)
{
    if(pClefPrivee==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        gmp_printf("Voici la clef privee: %Zd , %Zd \n", ClefPriveeGetu2(pClefPrivee), ClefPriveeGetn2(pClefPrivee));
    }
}


void ClefPriveeInitialiser(ClefPrivee **pClef)
{
    *pClef=(ClefPrivee *)malloc(sizeof(ClefPrivee));
    mpz_init((*pClef)->u);
    mpz_init((*pClef)->n);
}


mpz_ptr ClefPriveeGetn(ClefPrivee *pClef)
{
    if(pClef==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        return pClef->n;
    }
}


const mpz_ptr ClefPriveeGetn2(const ClefPrivee *pClef)
{
    if(pClef==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        return (const mpz_ptr)pClef->n;
    }
}


void ClefPriveeSetn(ClefPrivee *pClef, const mpz_t n1)
{
    if(pClef==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        mpz_set(pClef->n,n1);
    }
}


mpz_ptr ClefPriveeGetu(ClefPrivee *pClef)
{
    if(pClef==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        return pClef->u;
    }
}


const mpz_ptr ClefPriveeGetu2(const ClefPrivee *pClef)
{
    if(pClef==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        return (const mpz_ptr)pClef->u;
    }
}


void ClefPriveeSetu(ClefPrivee *pClef, const mpz_t u1)
{
    if(pClef==NULL)
    {
        printf("La clef privee n'a pas été allouée \n");
        exit(-1);
    }
    else
    {
        mpz_set(pClef->u,u1);
    }
}


void ClefPriveeLiberer(ClefPrivee * pClefPrivee)
{
    mpz_clear(pClefPrivee->u);
    mpz_clear(pClefPrivee->n);
    free(pClefPrivee);
    pClefPrivee=NULL;
}


void testRegressionClefPrivee()
{
    printf("test regression ClefPrivee \n");
    //mpz_t a;
    //mpz_t b;
    //mpz_t c;

    ClefPublique *clef;
    //ClefPubliqueInitialiser(&clef);
    // attention c et m doivent être premier entre eux
    //mpz_init_set_ui(a, 54454);
    //mpz_init_set_ui(b, 444);
    //mpz_init_set_ui(c, 3333);
    //ClefPubliqueSetn(clef, a);
    //ClefPubliqueSetc(clef, b);
    //ClefPubliqueSetm(clef, c);

    clef=ClefPubliqueGenerer();
    ClefPubliqueAfficher(clef);

    ClefPubliqueLiberer(clef);
}
